/*
 * \brief  Platform-independent part of server framework
 * \author Norman Feske
 * \date   2006-05-12
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#include <base/server.h>
#include <base/blocking.h>

using namespace Genode;


/***********************
 ** Server activation **
 ***********************/

void Server_activation_base::entry()
{
	Ipc_server srv(_snd_msg, _rcv_msg);
	_cap = srv;
	_cap_valid.unlock();

	/*
	 * Now, the capability of the server activation is initialized
	 * an can be passed around. However, the processing of capability
	 * invocations should not happen until activation-using server
	 * is completely initialized. Thus, we wait until server activation
	 * gets explicitly unblocked by calling its start() function.
	 */
	_delay_start.lock();

	while (1) {
		int opcode = 0;

		srv >> IPC_REPLY_WAIT >> opcode;

		/* set default return value */
		srv.ret(ERR_INVALID_OBJECT);

		/* atomically lookup and lock referenced object */
		{
			Lock::Guard lock_guard(_curr_obj_lock);

			_curr_obj = _ep ? _ep->obj_by_id(srv.badge()) : 0;
			if (!_curr_obj)
				continue;

			_curr_obj->lock();
		}

		/* dispatch request */
		try { srv.ret(_curr_obj->dispatch(opcode, srv, srv)); }
		catch (Blocking_canceled) { }

		_curr_obj->unlock();
		_curr_obj = 0;
	}
}


void Server_activation_base::leave_server_object(Server_object *obj)
{
	Lock::Guard lock_guard(_curr_obj_lock);

	if (obj == _curr_obj)
		cancel_blocking();
}


/***********************
 ** Server entrypoint **
 ***********************/

Server_entrypoint::Server_entrypoint(Cap_session *cap_session,
                                     Server_activation_base *a)
: _activation(0), _cap_session(cap_session)
{
	if (a) add_activation(a);
}


int Server_entrypoint::add_activation(Server_activation_base *t)
{
	/* l4v2 and linux supports only one activation per entry point */
	if (_activation) return -1;

	_activation = t;
	_activation->ep(this);

	return 0;
}


void Server_entrypoint::dissolve(Server_object *obj)
{
	/* make sure nobody is able to find this object */
	remove(obj);

	/*
	 * The activation may execute a blocking operation
	 * in a dispatch function. Before resolving the
	 * corresponding object, we need to ensure that
	 * it is no longer used by an activation. Therefore,
	 * we to need cancel an eventually blocking operation
	 * and let the activation leave the context of the
	 * object.
	 */
	_activation->leave_server_object(obj);

	/* wait until nobody is inside dispatch */
	obj->lock();

	/* now the object may be safely destructed */
}
